<template>
  <div class="test-content">
    <div class="chatting-context">
      <Card :bordered="false">
        <template #title>
          <Dropdown>
            <div class="dropddown-title">
              文本对话
              <Icon type="ios-arrow-down"></Icon>
            </div>
            <template #list>
              <DropdownMenu>
                <DropdownItem>文本对话</DropdownItem>
              </DropdownMenu>
            </template>
          </Dropdown>
        </template>
        <template #extra>
          <Space>
            <Icon type="ios-information-circle-outline" size="20" />
            <Icon class="back-icon" type="md-refresh" size="20" @click="emit('back')" />
          </Space>
        </template>

        <div ref="conversationRef" class="conversation-body">
          <ul v-if="anwList.length" class="reply">
            <li 
              v-for="(item, index) in anwList" 
              :key="index" 
              :class="{ 'user-answer': item.user }"
              v-memo="[item.value, item.user, item.createtime]"
            >
              <div class="user-info">
                <template v-if="!item.user">
                  <Icon type="logo-octocat" size="36" />
                  <span class="robot-name">{{ item.nickName }}</span>
                </template>

                <Icon v-else class="user-icon" type="md-person" size="28" />

                <span class="date">{{ item.createtime }}</span>
              </div>
              <div ref="answerRef" class="answer-value">
                <div v-html="item.value"></div>

                <ul class="search-list">
                  <li
                    v-for="(group, key) in item.suggest"
                    :key="key"
                    @click="getAnsData(group.title)"
                  >
                    {{ group.title }}
                  </li>
                </ul>
              </div>
            </li>
          </ul>

          <template v-else>
            <span class="default-label">试试这些例子</span>

            <ul class="default-question">
              <li v-for="(item, index) in defaultAnw" :key="index" @click="getAnsData(item.value)">
                {{ item.value }}
                <Icon type="arrow-right" class="arrow-right-icon" />
              </li>
            </ul>
          </template>
        </div>

        <div class="search-input">
          <Input
            v-model="searchValue"
            type="textarea"
            maxlength="1000"
            resize="none"
            show-word-limit
            :rows="3"
            placeholder="请输入您想要问的问题"
            @on-enter="getAnsData()"
          />

          <div class="search-btn">
            <!-- <Icon type="md-code" size="20" /> -->
            <Button type="primary" icon="md-paper-plane" :loading="loading" @click="getAnsData()" />
          </div>
        </div>
      </Card>
    </div>

    <div class="history-context">
      <span class="title">召回结果({{ formattedRecallList.length }})</span>

      <ul class="history-list">
        <li v-for="item in formattedRecallList" :key="item.id">
          <div class="progress-item">
            <span class="name">{{ item.title }}</span>
            <Progress :stroke-width="8" stroke-color="#6360b4" :percent="item.progressValue" />
          </div>

          <p class="desc">{{ item.content }}</p>
          <p class="desc">文档: {{ item.name }}</p>
        </li>

        <p v-if="!formattedRecallList.length" class="no-thing">暂无召回信息~</p>
      </ul>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick, onBeforeUnmount, computed } from 'vue'
import { Message } from 'view-ui-plus'
import moment from 'moment'
import { userStore } from '@/store/userInfo'
import { updateATagTarget } from '@/util'
import { debounce } from 'lodash-es'

import createWebSocket from '@/util/websocket'
import { marked } from 'marked'

// 确保 marked 配置正确
marked.setOptions({
  sanitize: false,
  renderer: new marked.Renderer(),
  gfm: true,
  breaks: true,
  smartLists: true,
  smartypants: false,
  xhtml: false
})

const { userInfo } = userStore()

const props = defineProps({
  knowDetails: Object
})

// 添加 loading 状态变量
const loading = ref(false)

const defaultAnw = [
  { value: '如何退出无障碍模式' },
  { value: '复制旧东西' },
  { value: '蓝牙耳机产品名称?' },
  { value: 'oppo平板名称' }
]

const emit = defineEmits(['back'])

const anwList = ref([])
const recallList = ref([])

const searchValue = ref('')
const conversationRef = ref(null)

// 添加用户滚动状态变量
const userHasScrolled = ref(false)

// 添加其他必要的变量
let messageObj = null
let isFirstChunk = true
let typewriterTimer = null
let timeoutTimer = null
let currentDisplayedLength = 0

// 将 socket 声明为 let 而不是 const，以便可以在 connectSocket 函数中重新赋值
let socket = null

const maxRetries = 3
let retryCount = 0

// 处理用户滚动事件
const handleUserScroll = debounce(() => {
  if (!conversationRef.value) return
  
  const { scrollTop, scrollHeight, clientHeight } = conversationRef.value
  // 如果用户向上滚动超过一定距离，标记为已手动滚动
  if (scrollHeight - scrollTop - clientHeight > 100) {
    userHasScrolled.value = true
  }
  
  // 如果用户已经滚动到底部，重置标记
  if (scrollHeight - scrollTop - clientHeight < 10) {
    userHasScrolled.value = false
  }
}, 100)

// 打字效果实现
const typewriterEffect = (fullText, element, startFrom = 0, speed = 30) => {
  if (typewriterTimer) {
    clearInterval(typewriterTimer)
  }
  
  let i = startFrom
  return setInterval(() => {
    if (i < fullText.length) {
      // 每次更新多个字符，减少DOM操作次数
      const chunkSize = Math.min(5, fullText.length - i)
      element.innerHTML = marked(updateATagTarget(fullText.substring(0, i + chunkSize)))
      i += chunkSize
    } else {
      clearInterval(typewriterTimer)
    }
  }, speed)
}

const getTimeNow = () => {
  return moment().format('YYYY-MM-DD HH:mm:ss')
}

// 格式化召回列表的计算属性
const formattedRecallList = computed(() => {
  return recallList.value.map(item => {
    // 确保 similarity 是数字
    const similarity = parseFloat(item.similarity || 0) * 100
    return {
      ...item,
      progressValue: Math.min(Math.max(similarity, 0), 100) // 确保值在 0-100 之间
    }
  })
})

const connectSocket = () => {
  try {
    socket = createWebSocket()
    
    socket.on('connect', () => {
      console.log('连接成功')
      retryCount = 0 // 重置重试计数
      // 发送初始化消息
      socket.send('new', {
        user: userInfo.id,
        orgi: userInfo.orgi,
        session: props.knowDetails.contextid,
        appid: props.knowDetails.appid,
        aiid: props.knowDetails.aihead?.id || 'd1858c2d1d524d24b3f3f4d110df6bc8'
      })
    })
    
    socket.on('error', (error) => {
      console.error('Socket error:', error)
      loading.value = false
      
      // 添加重试逻辑
      if (retryCount < maxRetries) {
        retryCount++
        setTimeout(() => {
          console.log(`尝试重新连接 (${retryCount}/${maxRetries})...`)
          connectSocket()
        }, 1000 * retryCount) // 递增重试延迟
      } else {
        Message.error('连接失败，请刷新页面重试')
      }
    })
    
    // 添加其他事件处理
    socket.on('knmessage', (message) => {
      // 如果消息为空且不是第一次接收，可能是结束标志
      if (!message && !isFirstChunk) {
        isFirstChunk = true
        loading.value = false // 接收结束，设置 loading 状态为 false
        return
      }
      
      // 如果消息为空且是第一次接收，忽略这个消息
      if (!message && isFirstChunk) {
        return
      }
      
      // 如果是第一个数据块，创建新的消息对象
      if (isFirstChunk) {
        // 清除超时计时器，因为已经开始接收响应
        if (timeoutTimer) {
          clearTimeout(timeoutTimer)
          timeoutTimer = null
        }
        
        // 清除之前的打字效果计时器
        if (typewriterTimer) {
          clearInterval(typewriterTimer)
        }
        
        messageObj = {
          value: '',
          rawContent: '',
          createtime: getTimeNow(),
          nickName: '知识库助手',
          suggest: []
        }
        anwList.value.push(messageObj)
        isFirstChunk = false
        currentDisplayedLength = 0
      }
      
      // 累加接收到的文本内容
      messageObj.rawContent += message
      
      // 使用 requestAnimationFrame 优化视觉更新
      requestAnimationFrame(() => {
        // 只有当用户没有手动滚动时，才自动滚动到底部
        if (conversationRef.value && !userHasScrolled.value) {
          conversationRef.value.scrollTop = conversationRef.value.scrollHeight
        }

        // 找到最后一个消息的DOM元素
        const messageElements = document.querySelectorAll('.answer-value div')
        if (messageElements.length > 0) {
          const lastMessageElement = messageElements[messageElements.length - 1]
          
          // 使用优化后的打字效果
          typewriterTimer = typewriterEffect(
            messageObj.rawContent,
            lastMessageElement,
            currentDisplayedLength,
            30
          )
          
          currentDisplayedLength = messageObj.rawContent.length
        }
      })
    })

    socket.on('disconnect', () => {
      loading.value = false
    })

    //找回结果
    socket.on('doc', (data) => {
      recallList.value = data || []
    })
  } catch (error) {
    console.error('创建WebSocket时出错:', error)
    Message.error('连接失败，请刷新页面重试')
  }
}

// 修改 getAnsData 函数，确保使用正确的 socket 变量
const getAnsData = (val) => {
  // 检查socket是否存在
  if (!socket) {
    Message.error('连接未建立，请刷新页面重试')
    return
  }
  
  // 重置用户滚动标记，因为用户发送新消息时通常期望看到回复
  userHasScrolled.value = false
  
  const searchText = val || searchValue.value

  if (!searchText.trim()) {
    return (searchValue.value = '')
  }

  // 设置 loading 状态为 true
  loading.value = true
  
  // 清除之前的超时计时器
  if (timeoutTimer) {
    clearTimeout(timeoutTimer)
  }
  
  // 设置新的超时处理
  timeoutTimer = setTimeout(() => {
    if (loading.value) {
      loading.value = false
      if (isFirstChunk === false) {
        isFirstChunk = true
        Message.warning('响应时间过长，请重试')
      }
    }
  }, 30000)

  anwList.value.push({
    value: searchText,
    user: true,
    createtime: getTimeNow()
  })
  nextTick(()=>{
    conversationRef.value.scrollTop = conversationRef.value.scrollHeight
  })

  try {
    socket.send({
      appid: props.knowDetails.appid,
      userid: userInfo.id,
      username: userInfo.username,
      channel: 'webim',
      debug: true,
      type: 'message',
      contextid: props.knowDetails.contextid,
      orgi: userInfo.orgi,
      message: searchText
    })
  } catch (error) {
    console.error('发送消息时出错:', error)
    loading.value = false
    Message.error('发送失败，请重试')
    if (timeoutTimer) {
      clearTimeout(timeoutTimer)
      timeoutTimer = null
    }
  }

  searchValue.value = ''
}

onMounted(() => {
  // 初始连接
  connectSocket()
  
  if (conversationRef.value) {
    conversationRef.value.addEventListener('scroll', handleUserScroll)
  }
})

onBeforeUnmount(() => {
  if (socket) {
    try {
      socket.destory()
    } catch (error) {
      console.error('关闭WebSocket时出错:', error)
    }
  }
  
  if (conversationRef.value) {
    conversationRef.value.removeEventListener('scroll', handleUserScroll)
  }
  
  // 清除超时计时器
  if (timeoutTimer) {
    clearTimeout(timeoutTimer)
  }
  
  // 清除打字效果计时器
  if (typewriterTimer) {
    clearInterval(typewriterTimer)
  }
})
</script>

<style scoped lang="scss">
.test-content {
  display: flex;
  height: calc(100% - 50px);
  .chatting-context {
    flex: 1;
    margin-right: 10px;
    background-color: #f3f4f7;
    min-width: 400px;
    .ivu-card {
      width: 100%;
      // height: 100%;
      border-radius: 0;
      &:hover {
        box-shadow: unset;
      }
      :deep(.ivu-card-body) {
        background-color: #f3f4f7;
        padding: 20px 0 0;
        .search-input {
          position: relative;
          margin-top: 16px;
          .ivu-input-type-textarea {
            .ivu-input {
              resize: none;
              padding-right: 40px;
            }
            .ivu-input-word-count {
              right: 52px;
            }
          }
          .search-btn {
            position: absolute;
            right: 6px;
            bottom: 4px;
            display: flex;
            flex-direction: column;
            .ivu-btn {
              width: 40px;
              height: 32px;
              line-height: 0;
            }
            .ivu-icon-md-code {
              transform: rotate(-45deg);
              margin-bottom: 15px;
              margin-left: 15px;
            }
          }
        }
      }
    }
    .back-icon {
      cursor: pointer;
    }
  }
  .history-context {
    width: 320px;
    min-width: 320px;
    background-color: #fff;
    padding: 12px;
    .title {
      color: #17233d;
      font-size: 16px;
      font-weight: bold;
    }

    .history-list {
      margin-top: 16px;
      height: calc(100% - 34px);
      overflow-y: auto;
      margin-right: -12px;
      padding-right: 12px;
      position: relative;
      > li {
        border: 1px solid #ddd;
        border-radius: 4px;
        padding: 12px;
        &:nth-child(n + 2) {
          margin-top: 16px;
        }
        .progress-item {
          display: flex;
          .name {
            width: 50px;
          }
          :deep(.ivu-progress) {
            flex: 1;
            .ivu-progress-outer {
              padding-right: 80px;
              margin-right: -80px;
            }
            .ivu-progress-text-inner {
              &::before {
                content: '相似值: ';
              }
            }
          }
        }
        .desc {
          color: #666;
          margin-top: 8px;
        }
      }

      .no-thing {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        font-size: 14px;
        color: #999;
      }
    }
  }
}

.conversation-body {
  padding: 0 20px;
  // padding: 20px 25%;
  // width: 800px;
  min-width: 200px;
  // max-width: 800px;
  height: calc(100vh - 307px);
  // margin: 16px auto;
  overflow-y: auto;
  .reply {
    > li {
      text-align: left;
      &:nth-child(n + 2) {
        margin-top: 24px;
      }
      &.user-answer {
        text-align: right;
        .user-info {
          flex-direction: row-reverse;
          .user-icon {
            width: 36px;
            height: 36px;
            border-radius: 50%;
            background-color: #0066ff;
            text-align: center;
            line-height: 36px;
            color: #fff;
            margin: 0 0 0 8px;
          }
        }
        .answer-value {
          // background-color: #0066ff;
          padding: 6px 12px;
          border-radius: 4px;
          // color: #fff;
          text-align: left;
          margin: -8px 44px 0 0;
        }
      }
      .user-info {
        display: flex;
        .robot-name {
          margin-right: 4px;
          color: #0066ff;
        }
        .ivu-icon {
          margin-right: 8px;
        }
        .date {
          color: #666;
        }
      }
      .answer-value {
        max-width: 80%;
        display: inline-block;
        color: #545658;
        font-size: 16px;
        background-color: #fff;
        border-radius: 4px;
        padding: 6px 12px;
        margin: -8px 0 0 44px;
        .search-list {
          li {
            color: #0066ff;
            cursor: pointer;
            margin-top: 8px;
            &:hover {
              opacity: 0.9;
            }
          }
        }
      }
      .unfold {
        color: #168fff;
        cursor: pointer;
        font-size: 14px;
        &:hover {
          opacity: 0.9;
        }
      }
    }
  }
  .default-label {
    color: #7e8088;
  }
  .default-question {
    display: flex;
    flex-wrap: wrap;
    li {
      margin: 16px 16px 0 0;
      border: 1px solid #e2e7ed;
      border-radius: 4px;
      padding: 4px 8px;
      color: #545658;
      cursor: pointer;
      background-color: #f8f8f8;
      &:hover {
        opacity: 0.9;
      }
      .arrow-right-icon {
        margin-left: 8px;
        font-size: 12px;
        transform: translateY(-1px);
      }
    }
  }
}
</style>
